package com.example.csmallpassport.config;

import com.alibaba.fastjson.JSON;
import com.example.csmallpassport.filter.JwtAuthorizationFilter;
import com.example.csmallpassport.web.JsonResult;
import com.example.csmallpassport.web.ServiceCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启基于方法的权限检查,判断有没有权力执行此方法
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthorizationFilter jwtAuthorizationFilter;

    public SecurityConfiguration() {
        log.debug("创建配置类对象：SecurityConfiguration");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        //再UserDetails中构建密码所使用的密码编辑器模式，
        //此处NoOpPasswordEncoder.getInstance();表示不对密码进行加密处理
        //return NoOpPasswordEncoder.getInstance();

        //此处密码编码器使用的时BCrypt进行编码
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //不用认证可以直接访问的url(白名单)
        //所有的配置都是以/开头的
        //*表示允许全部条件，但是只允许单层级 /admins/* 只可以访问到/damins/xxx文件 /admins/xxx/xxx是不可以进行访问的
        //**表示统配若干层级的任意资源，/admins/**,就可以访问到 /admins/xxxx文件也可以访问到/admins/xxxx/xxxx文明考吗
        String[] urls = {
                "/doc.html",
                "/**/*.css",
                "/**/*.js",
                "/swagger-resources",
                "/v2/api-docs",
                "/favicon.ico",
                "/admins/login",
        };

        //配置框架自带的CorsFilter方法过滤器，此过滤器会对OPTIONS请求直接方法
        http.cors();

        //再SecurityConfiguration中添加此配置后
        // ，POST类型的请求也可以请求到业务
        http.csrf().disable();

        /*
         * 配置时，个请求的授权访问遵循"第一匹配原则"既根据代码从上至下，以第一次匹配到的规则为准
         * 所以再配置时，必须将更加精准的配置写在前面，覆盖更大的匹配配置应该写在后面
         * */
        //匹配URL的授权访问
        http.authorizeRequests()
                .mvcMatchers(urls) //匹配某些请求
                .permitAll()//直接许可,既：不需要通过认证就可以访问
                .anyRequest() //匹配任何请求
                .authenticated(); //以上匹配到的请求必须是"已经通过认证的"

        //将jwtAuthorizationFilter过滤器添加到UsernamePasswordAuthentionFilter过滤器之前
        http.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);

        //将Session的策略设置为"从不使用":STATELESS 无状态， 既从不使用session , NEVER 从不主动创建Session(不能解决当前问题)
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        //当客户端提交的请求没有携带JWT时，请求的目标却是需要认证的资源，则服务端会相应403错误信息
        http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest Request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                response.setContentType("application/json; charset=utf-8;");
                String message  = "未检测到登录信息,请登录";
                JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_UNAUTHORIZED, message);
                String jsonResultString = JSON.toJSONString(jsonResult);
                PrintWriter writer = response.getWriter();
                writer.println(jsonResultString);
                writer.close();
            }
        });
        // 调用formLogin()表示启用登录和等处页面，如果为调用此方法，则没有登录登出页面，但是此方法只是开始页面，不会强制登陆
        // http.formLogin();
        // super.configure(http);

/*        http
                .csrf()
                .disable()
                .formLogin()
                .and()
                .authorizeRequests()
                .mvcMatchers()
                .permitAll()
                .anyRequest()
                .authenticated();*/

    }


}
